package com.dukk.gis.tools;

import com.dukk.gis.tools.extensions.projections.EquidistantAzimuthalProjectionTool;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.operation.buffer.BufferOp;
import org.locationtech.jts.operation.buffer.BufferParameters;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

/**
 * 扩圈工具类
 */
public class BufferTool {


    /**
     * 圆角 扩圈 （上下是矩形，左右是圆角）
     * @param geometry
     * @param distance X米
     * @return
     */
    public static Geometry bufferCapRound(Geometry geometry, double distance ){
        return buffer(geometry, distance, BufferParameters.CAP_ROUND);
    }

    /**
     * 扩圈(上下左右四个方向,外接多边形)
     * @param geometry
     * @param distance X米
     * @return
     */
    public static Geometry bufferCapSquare(Geometry geometry, double distance ){
        return buffer(geometry, distance, BufferParameters.CAP_SQUARE);
    }

    /**
     *  上下扩圈(扩完是矩形)
     * @param geometry
     * @param distance X米
     * @return
     */
    public static Geometry bufferCapFat(Geometry geometry, double distance){
        return buffer(geometry, distance, BufferParameters.CAP_FLAT);
    }


    /**
     *
     * @param geometry
     * @param distance x米
     * @param endCapStyle
     * @return
     */
    public static Geometry buffer(Geometry geometry, double distance, int endCapStyle){

        BufferParameters bufferParameters = new BufferParameters();
        bufferParameters.setEndCapStyle(endCapStyle);

        BufferOp bufOp = new BufferOp(geometry, bufferParameters);

        //1KM大约等于多少度
        double kmToDegreeDistance = DistanceTool.cmToDegree(distance*100);

        return bufOp.getResultGeometry(kmToDegreeDistance);

    }


    /**
     * 获取扩圈后的geo
     * @param geometry
     * @param leftDistance 左扩 x米
     * @param   rightDistance 右扩 x米
     * @return
     */
    public static Geometry bufferGeoLeftRight(Geometry geometry, double leftDistance, double rightDistance) {


        BufferParameters bufferParameters = new BufferParameters();
        bufferParameters.setSingleSided(true);

        BufferOp bufOp = new BufferOp(geometry, bufferParameters);

        //1KM大约等于多少度
        double kmToDegreeLeft = DistanceTool.cmToDegree(leftDistance*100);
        double kmToDegreeRight = DistanceTool.cmToDegree(rightDistance*100);

        Geometry geometryLeft = bufOp.getResultGeometry(kmToDegreeLeft); //左
        Geometry geometryRight = bufOp.getResultGeometry(-kmToDegreeRight); //右 -1

        Geometry bufferGeo = geometryLeft.union(geometryRight);

        return bufferGeo;



    }

    /**
     * 获取扩圈后的geo
     * @param geometry
     * @param distance 左扩 x 米
     * @return
     */
    public static Geometry bufferGeoLeft(Geometry geometry, double distance)  {

        BufferParameters bufferParameters = new BufferParameters();
        bufferParameters.setSingleSided(true);

        BufferOp bufOp = new BufferOp(geometry, bufferParameters);

        //1KM大约等于多少度
        double kmToDegree = DistanceTool.cmToDegree(distance*100);

        Geometry bufferGeo = bufOp.getResultGeometry(kmToDegree);


        return bufferGeo;

    }

    /**
     * 获取扩圈后的geo
     * @param geometry
     * @param distance 右扩 x米
     * @return
     */
    public static Geometry bufferGeoRight(Geometry geometry, double distance) {

        BufferParameters bufferParameters = new BufferParameters();
        bufferParameters.setSingleSided(true);

        BufferOp bufOp = new BufferOp(geometry, bufferParameters);

        //1KM大约等于多少度
        double kmToDegree = DistanceTool.cmToDegree(distance*100);

        Geometry bufferGeo = bufOp.getResultGeometry(-kmToDegree);

        return bufferGeo;

    }

    /**
     * 严格按照距离投影
     * https://stackoverflow.com/questions/36455020/geotools-bounding-box-for-a-buffer-in-wgs84
     * @param geometry
     * @param distanceInMeters
     * @return
     * @throws FactoryException
     * @throws TransformException
     */
    public static Geometry buffer(Geometry geometry, double distanceInMeters) throws FactoryException, TransformException {
        String code = "AUTO:42001," + geometry.getCentroid().getCoordinate().x + "," + geometry.getCentroid().getCoordinate().y;
        CoordinateReferenceSystem auto = CRS.decode(code);

        MathTransform crs84ToAuto = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
        MathTransform crsAutoTo84 = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);

        Geometry pGeom = JTS.transform(geometry, crs84ToAuto);
        Geometry pBufferedGeom = pGeom.buffer(distanceInMeters);
        return JTS.transform(pBufferedGeom, crsAutoTo84);
    }

    /**
     * 根据方位等距投影转换进行扩buffer处理
     * @param geometry
     * @param distanceInMeters
     * @return
     */
    public static Geometry bufferEquidistantAzimuthal(Geometry geometry, double distanceInMeters) {

        EquidistantAzimuthalProjectionTool equidistantAzimuthalProjectionTool = new EquidistantAzimuthalProjectionTool();
        Geometry gProject = equidistantAzimuthalProjectionTool.project((LineString) geometry);

        BufferParameters bufferParameters = new BufferParameters();
        bufferParameters.setEndCapStyle(BufferParameters.CAP_FLAT);

        BufferOp bufOp = new BufferOp(gProject, bufferParameters);
        Geometry bufPro = bufOp.getResultGeometry(distanceInMeters);

        return equidistantAzimuthalProjectionTool.inverseProject((Polygon) bufPro);

    }


    public static void main(String[] args) throws Exception {
      /* Geometry geometry = WktTool.wktToGeo("LINESTRING (109.333 22.8535, 109.36 22.85, 109.38 22.85, 109.4 22.85, 109.4125 22.849, 109.4335 22.8475)");
       Geometry geometry = WktTool.wktToGeo("LINESTRING (109.404 22.8385, 109.4045 22.857, 109.4045 22.87, 109.405 22.8825, 109.4065 22.8925, 109.4085 22.9035)");*/
       Geometry geometry = WktTool.wktToGeo("LINESTRING (111.1775 23.5295, 111.1775 23.514, 111.1785 23.5025, 111.1775 23.4915, 111.175 23.4855, 111.1705 23.475, 111.169 23.4675, 111.169 23.4595)");

        System.out.println(BufferTool.buffer(geometry, 0.2).toText());
        System.out.println(BufferTool.bufferEquidistantAzimuthal(geometry,0.2).toText());

        Geometry jtsBuffer = BufferTool.bufferCapFat(geometry, 0.2);
        System.out.println(jtsBuffer);

        EquidistantAzimuthalProjectionTool equidistantAzimuthalProjectionTool = new EquidistantAzimuthalProjectionTool();
        System.out.println(equidistantAzimuthalProjectionTool.project((Polygon) jtsBuffer));


    }


}
